Skip to content

feat: Canvas Workspace — infinite canvas design tool#1

Open
csacsi wants to merge 50 commits into
mainfrom
feature/canvas-workspace
Open

feat: Canvas Workspace — infinite canvas design tool#1
csacsi wants to merge 50 commits into
mainfrom
feature/canvas-workspace

Conversation

@csacsi
Copy link
Copy Markdown
Contributor

@csacsi csacsi commented Mar 25, 2026

Summary

Transforms PinLaunch from a landing page generator into a full design tool with an infinite canvas workspace at /canvas.

  • Infinite canvas with pan (two-finger scroll), pinch-to-zoom, and dot grid background
  • 5 node types: Shape & Widget (roughjs hand-drawn SVG), Image (upload + drag-and-drop), Document (inline markdown editing), Artboard (iframe preview of generated sites)
  • Lasso selection with live green rectangle — nodes highlight/unhighlight interactively as lasso moves
  • Multi-drag — drag one selected node and all selected nodes move together in real-time
  • Resize handles on non-artboard nodes (4 corner handles, Shift for aspect ratio lock, Alt for center resize)
  • Toolbar with Shapes, Widgets, Image upload, Document creation
  • Sidebar with Setup/Chat/Inspector tabs — full brief generation flow (CreativeDirectorTerminal → BriefEditor → ClaudeTerminal → artboard on canvas)
  • Inspector panel with node-specific controls (position, size, viewport preset, label, colors, alt text, exclude from export)
  • Minimap with bounding-box overview and viewport indicator
  • Context menu (right-click) with Bring to Front / Send to Back / Delete
  • Undo/redo (Cmd+Z / Cmd+Shift+Z) with 50-step history
  • Auto-save to SQLite (debounced 500ms) + beforeunload fallback
  • Header with ProjectsDropdown — selecting a project creates an artboard on canvas

New files

  • src/app/canvas/page.tsx — Canvas page
  • src/components/canvas/ — 11 components (Canvas, CanvasNode, CanvasToolbar, Sidebar, InspectorPanel, Minimap, ShapeNode, WidgetNode, ImageNode, DocumentNode, ArtboardNode)
  • src/lib/canvas-types.ts + canvas-state.ts + tests
  • src/app/api/canvas/ + src/app/api/uploads/ — API routes

Modified files

  • src/lib/db.ts — added canvas_state table
  • src/app/page.tsx — redirects to /canvas
  • src/components/Header.tsx — logo links to /canvas

Test plan

  • npm run test — 68/68 passing
  • npm run build — clean
  • Manual: create shapes/widgets via toolbar, verify roughjs rendering
  • Manual: drag-and-drop image from Finder, verify upload + display
  • Manual: lasso select multiple nodes, multi-drag together
  • Manual: resize nodes with corner handles (Shift/Alt modifiers)
  • Manual: run brief generation flow end-to-end → artboard appears
  • Manual: undo/redo, delete, context menu actions
  • Manual: pan (scroll) and zoom (pinch) on trackpad
  • Manual: refresh page → nodes persist from SQLite

🤖 Generated with Claude Code

csacsi and others added 30 commits March 25, 2026 16:48
Design spec for the /canvas route — an infinite canvas workspace that
evolves PinLaunch from a landing page generator into a full design tool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
13-task implementation plan covering: types/state management, DB/API
routes, canvas core with pan/zoom, shape/widget/image/document/artboard
nodes, toolbar, sidebar, inspector, minimap, and integration tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
csacsi and others added 20 commits March 25, 2026 22:15
- Add canvas-node CSS class to node wrappers and exclude from
  react-zoom-pan-pinch panning so nodes can be dragged independently
- Use local dragOffset state for smooth visual drag without flooding
  the undo stack on every mousemove
- Commit position to canvas state only on mouseUp via onMoveRef
- Add e.preventDefault() to stop browser default drag behavior

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a node drag starts, set panningDisabled=true on the
TransformWrapper so react-zoom-pan-pinch doesn't compete
for mouse events. Re-enable on mouseUp.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Selected nodes now show a translucent green outline and fill on the
wrapper div. Removed duplicate blue selection outlines from individual
node components.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drag on empty canvas area draws a translucent green selection
rectangle. On mouseUp, all nodes intersecting the rectangle
become selected. Panning is disabled during lasso.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge main into feature/canvas-workspace. Resolved page.tsx conflict
(keep canvas redirect). Updated Sidebar to use new GeneratePanel
interface (onPrepareBrief + preparing props).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Header component to canvas page with ProjectsDropdown
- Selecting a project creates an artboard on the canvas
- Default provider changed to 'claude' (Gemini removed from main)
- Fetch provider from /api/settings on mount
- Update Sidebar to use new GeneratePanel interface (onPrepareBrief)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sidebar now manages the complete brief flow with phases:
- idle: shows PinBoard, GitHubPanel, PresetsPanel, GeneratePanel
- preparing: shows CreativeDirectorTerminal (streams brief generation)
- brief: shows BriefEditor (edit colors, typography, sections)
- building: shows ClaudeTerminal (streams site generation)

On build complete, artboard is created on the canvas. A "Back"
button allows returning to idle from any phase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Selected non-artboard nodes show 4 green corner handles (nw/ne/sw/se).
Dragging a handle resizes the node visually during drag (local state),
then commits the final size on mouseUp. Minimum size enforced at 30px.
Artboards are excluded (viewport preset only).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Custom wheel handler replaces default scroll-to-zoom:
- Two-finger trackpad scroll → pans the canvas (deltaX/deltaY)
- Pinch gesture (ctrlKey + deltaY) → zooms toward cursor position
- Library's built-in wheel zoom disabled to prevent conflicts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
During node resize:
- Hold Shift to maintain the original aspect ratio
- Hold Alt to resize symmetrically from the center
- Both modifiers can be combined

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lasso selection:
- Nodes highlight interactively as the lasso rectangle moves over them
- Nodes deselect when the lasso moves away from them
- Escape cancels the lasso and clears selection

Selection behavior:
- Clicking already-selected node keeps multi-selection (for drag)
- Shift+click toggles individual nodes in/out of selection
- Escape clears all selection

Multi-drag:
- Dragging one of multiple selected nodes moves all of them
- Each node moves by the same relative delta as the cursor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When dragging one of multiple selected nodes, all other selected
nodes now visually follow in real-time during the drag, not just
on mouseUp. Uses externalDragOffset prop to communicate the
drag delta from the source node to sibling selected nodes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resize handles are now 12px on screen regardless of zoom level.
Selection outline thickness and offset also scale inversely with
zoom so they remain visually consistent at any zoom level.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A click (mousedown+mouseup without drag) on empty canvas area
now sends null to onLassoUpdate, clearing the selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace top text toolbar with bottom-centered pill-shaped toolbar:
- 5 icon buttons: Website, Image, Document, Wireframe, Prompt
- Wireframe button opens submenu above pill (shapes + widgets grid)
- Prompt (sparkles) button opens expandable input above pill
- Enter/Generate sends prompt to sidebar brief flow
- Website button opens sidebar generate panel
- Inline SVG icons, tooltips on hover, click-outside to close
- Sidebar now accepts externalPrompt prop for toolbar integration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…oning

Toolbar was inside the Canvas transform area, making it scroll with
content. Now it's a sibling of Canvas in the container div, so
absolute bottom-4 positions it at the viewport bottom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant